package com.black.utils.converter;

import java.nio.ByteBuffer;

/**
 * @author ylx
 */
public class ByteConverter {
    /**
     * 将一个整数转换为字节数组。
     * 此方法用于将整数的每个字节拆分并存储在一个字节数组中，以大端字节序表示。
     * 大端字节序意味着最高位（最左边）的字节位于数组的起始位置。
     *
     * @param value 要转换的整数。
     * @return 包含整数每个字节的字节数组。
     */
    public static byte[] intToBytes(int value) {
        // 按大端字节序将整数拆分为字节，并存储在字节数组中
        return new byte[]{
                (byte) (value >> 24), // 获取最高位字节
                (byte) (value >> 16), // 获取次高位字节
                (byte) (value >> 8),  // 获取下一位字节
                (byte) value         // 获取最低位字节
        };
    }


    /**
     * 将字节数组转换为整数。
     * 此方法用于将四个字节组成的字节数组按照大端法（最高字节在最前面）转换为一个32位整数。
     * 每个字节都被视为无符号整数，并通过与0xFF进行位与操作来确保只保留低8位。
     * 字节的顺序通过位移操作组合成最终的整数。
     *
     * @param bytes 字节数组，长度必须至少为4，且数组的索引[0]到[3]分别代表整数的高到低的字节。
     * @return 转换后的整数。
     */
    public static int bytesToInt(byte[] bytes) {
        // 将第一个字节与0xFF进行位与操作以确保只保留低8位（最高字节）左移24位，
        return ((bytes[0] & 0xFF) << 24) |
                // 将第二个字节左移16位
                ((bytes[1] & 0xFF) << 16) |
                // 将第三个字节左移8位
                ((bytes[2] & 0xFF) << 8) |
                // 将第四个字节（最低字节）不做位移
                (bytes[3] & 0xFF);
    }


    /**
     * 将长整型数值转换为字节数组。
     * 此方法用于将一个长整型（long）值转换为由8个字节组成的数组，每个字节代表长整型值的一部分。
     * 转换过程中，长整型值按位右移，并通过类型转换为字节，以确保高字节在前，低字节在后。
     * 这种转换方式适用于需要将长整型值存储或传输为字节序列的场景。
     *
     * @param value 需要转换的长整型值。
     * @return 一个包含8个字节的数组，代表了原始长整型值的二进制表示。
     */
    public static byte[] longToBytes(long value) {
        // 通过右移和类型转换，将长整型值分割为8个字节
        return new byte[]{
                (byte) (value >> 56), // 取得长整型值的最高字节
                (byte) (value >> 48), // 取得次高字节
                (byte) (value >> 40), // 取得次次高字节
                (byte) (value >> 32), // 取得次次次高字节
                (byte) (value >> 24), // 取得次次次次高字节
                (byte) (value >> 16), // 取得次次次次次高字节
                (byte) (value >> 8),  // 取得次次次次次次高字节
                (byte) value         // 取得最低字节
        };
    }


    /**
     * 将字节数组转换为长整型数值。
     * 此方法用于将字节顺序从字节数组的顺序转换为长整型的顺序。
     * 每个字节被看作无符号字节，并按照从高位到低位的顺序组合成长整型。
     *
     * @param bytes 输入的字节数组，长度必须至少为8个字节。
     * @return 转换后的长整型数值。
     */
    public static long bytesToLong(byte[] bytes) {
        // 从字节数组中提取每个字节，并将其转换为长整型。
        // 使用位移操作符将每个字节移动到正确的位数，然后通过按位或操作符组合这些值。
        // 这样做的目的是为了将字节的顺序从数组顺序转换为长整型的顺序。
        return ((long) (bytes[0] & 0xFF) << 56) |
                ((long) (bytes[1] & 0xFF) << 48) |
                ((long) (bytes[2] & 0xFF) << 40) |
                ((long) (bytes[3] & 0xFF) << 32) |
                ((long) (bytes[4] & 0xFF) << 24) |
                ((long) (bytes[5] & 0xFF) << 16) |
                ((long) (bytes[6] & 0xFF) << 8) |
                ((long) (bytes[7] & 0xFF));
    }


    /**
     * 将short类型转换为字节数组。
     * 此方法用于将一个short值转换为两个字节的数组，高字节在前，低字节在后。
     * Java中short类型是16位的，因此需要两个字节来表示。
     * 这种转换对于处理多字节数据类型（如short、int、long）非常有用，特别是在网络传输或文件读写中，
     * 因为这些场景中通常需要将大整数分解为字节序列进行操作。
     *
     * @param value 待转换的short值。
     * @return 一个包含两个字节的数组，表示给定short值的字节序列。
     */
    public static byte[] shortToBytes(short value) {
        // 转换short值的高字节和低字节为byte，并存储到数组中
        return new byte[]{
                (byte) (value >> 8), // 高字节
                (byte) value         // 低字节
        };
    }


    /**
     * 将两个字节转换为一个短整型。
     * 此方法用于将网络字节序（大端在前）的两个字节转换为Java的本地字节序。
     * <p>
     * 字节序是指在多字节数据（如短整型、整型或长整型）中，哪个字节位于最前面。
     * 网络字节序是TCP/IP协议规定的一种字节序，用于在不同架构的系统之间进行数据交换。
     * Java的本地字节序可能因系统架构的不同而不同，但在Intel x86架构和AMD64架构上通常是小端在前。
     *
     * @param bytes 一个包含两个字节的数组，第一个字节是高字节，第二个字节是低字节。
     * @return 转换后的短整型值。
     */
    public static short bytesToShort(byte[] bytes) {
        // 将第一个字节（高字节）左移8位，并与0xFF进行位与操作，以确保只保留低8位。
        // 然后将结果与第二个字节（低字节）进行位或操作，合成一个短整型值。
        return (short) (((bytes[0] & 0xFF) << 8) |
                (bytes[1] & 0xFF));
    }

    /**
     * 将单精度浮点数转换为字节数组。
     * <p>
     * 此方法用于将一个float类型的数值转换为byte数组，目的是为了进行网络传输或存储等操作。
     * 浮点数到字节数组的转换保证了数据的准确性和可逆性，可以通过byteToFloat方法将字节数组转换回原始的浮点数。
     * <p>
     * 使用了Java的ByteBuffer类来完成转换，ByteBuffer的allocate方法分配了一个大小为4的字节缓冲区（因为float类型占用4个字节），
     * 然后通过putFloat方法将值放入缓冲区，最后通过array方法获取缓冲区的字节数组。
     *
     * @param value 待转换的单精度浮点数。
     * @return 转换后的字节数组。
     */
    public static byte[] floatToBytes(float value) {
        // 分配一个大小为4的ByteBuffer，放入浮点数value，然后返回转换后的字节数组。
        return ByteBuffer.allocate(4).putFloat(value).array();
    }

    /**
     * 将字节数组转换为浮点数。
     * 该方法通过使用ByteBuffer类的包装功能，将字节数组直接转换为浮点数。
     * 这种转换是基于Java的内存字节序（Big-Endian）进行的，确保了字节数据可以正确解析为浮点数。
     *
     * @param bytes 字节数组，代表要转换的浮点数。
     * @return 转换后的浮点数。
     */
    public static float bytesToFloat(byte[] bytes) {
        // 使用ByteBuffer的wrap方法将字节数组包装成ByteBuffer实例，并通过getFloat方法读取浮点数。
        return ByteBuffer.wrap(bytes).getFloat();
    }

    /**
     * 将double类型的浮点数转换为字节数组。
     * <p>
     * 该方法通过使用ByteBuffer来处理类型转换，确保了转换的准确性和效率。
     * ByteBuffer的分配空间大小是8字节，因为double类型的大小是8字节。
     * 使用putDouble方法将值放入ByteBuffer中，然后通过array方法获取底层字节数组。
     * 这种方法适用于需要将double类型数据进行二进制传输或存储的场景。
     *
     * @param value 待转换的double类型浮点数。
     * @return 转换后的字节数组。
     */
    public static byte[] doubleToBytes(double value) {
        // 分配一个8字节的ByteBuffer，用于存储double值
        return ByteBuffer.allocate(8).putDouble(value).array();
    }

    /**
     * 将字节数组转换为双精度浮点数。
     * <p>
     * 该方法通过利用ByteBuffer的包装功能，将字节数组直接转换为双精度浮点数。
     * 这种转换考虑了字节序的问题，确保在不同平台（大小端不同）上的兼容性。
     *
     * @param bytes 字节数组，代表一个双精度浮点数的二进制表示。
     * @return 转换后的双精度浮点数。
     */
    public static double bytesToDouble(byte[] bytes) {
        // 使用ByteBuffer的wrap方法将字节数组包装成ByteBuffer实例，并通过getDouble方法读取双精度浮点数。
        return ByteBuffer.wrap(bytes).getDouble();
    }

    /**
     * 将一个char值转换为对应的字节序列。
     * 该方法用于将一个Unicode字符转换为两个字节的形式，适用于某些需要将字符编码为字节序列的场景。
     *
     * @param value 待转换的char值。
     * @return 一个包含两个字节的数组，表示给定char值的字节序列。
     */
    public static byte[] charToBytes(char value) {
        // 字节序列为大端在前，先转换高8位
        return new byte[]{
                (byte) (value >> 8), // 右移8位，获取高8位字节
                (byte) value         // 直接转换为字节，获取低8位
        };
    }

    /**
     * 将两个字节转换为一个字符。
     * 此方法用于将字节数据解析为字符，特别适用于处理多字节字符编码（如UTF-16）的情况。
     * 它通过将第一个字节的高8位与第二个字节的低8位组合来创建一个16位的字符。
     *
     * @param bytes 一个包含两个字节的数组，这两个字节将被组合成一个字符。
     * @return 返回由输入字节组合成的字符。
     */
    public static char bytesToChar(byte[] bytes) {
        // 将第一个字节的值左移8位，并与第二个字节的值进行按位或操作，以组合成一个16位的值。
        // 这里的 & 0xFF 操作用于将字节的值转换为无符号整数，因为Java中字节是 signed 的。
        return (char) (((bytes[0] & 0xFF) << 8) |
                (bytes[1] & 0xFF));
    }


    public static byte[] booleanToBytes(boolean value) {
        return new byte[]{
                (byte) (value ? 1 : 0)
        };
    }

    public static boolean bytesToBoolean(byte[] bytes) {
        return bytes[0] != 0;
    }

    /**
     * 将十六进制字符串转换为字节数组。
     *
     * @param hex 十六进制字符串。字符串中的字母可以是大写或小写。
     * @return 与十六进制字符串对应的字节数组。
     */
    public static byte[] hexStringToBytes(String hex) {
        // 将字符串转换为大写字符数组，以便后续处理
        char[] charArray = hex.toUpperCase().toCharArray();
        int count = 0;

        // 统计字符数组中有效的十六进制字符数量
        for (char c : charArray) {
            if (c >= '0' && c <= '9' || c >= 'A' && c <= 'F') {
                ++count;
            }
        }

        // 根据有效的十六进制字符数量计算字节数组的长度
        byte[] bytes = new byte[count + 1 >> 1];
        int index = count & 1;

        // 实际转换十六进制字符为字节
        for (char c : charArray) {
            if (c >= '0' && c <= '9') {
                // 如果是数字，直接计算其对应的字节值
                bytes[index >> 1] = (byte) (bytes[index >> 1] << 4);
                bytes[index >> 1] = (byte) (bytes[index >> 1] | c - 48);
            } else {
                if (c < 'A' || c > 'F') {
                    continue;
                }

                // 如果是字母，同样计算其对应的字节值，但需要考虑A-F的转换
                bytes[index >> 1] = (byte) (bytes[index >> 1] << 4);
                bytes[index >> 1] = (byte) (bytes[index >> 1] | c - 65 + 10);
            }
            ++index;
        }

        return bytes;
    }

    /**
     * 将字节数组转换为十六进制字符串。
     * 此函数用于将二进制数据表示为更容易阅读和理解的十六进制字符串形式。
     * 每个字节被转换为两个十六进制字符，确保输出字符串的长度是输入字节数的两倍。
     *
     * @param bytes 输入的字节数组，可以包含任意二进制数据。
     * @return 返回一个字符串，表示输入字节数组的十六进制表示。
     */
    public static String bytesToHexString(byte[] bytes) {
        // 使用StringBuilder来高效地构建最终的十六进制字符串
        StringBuilder sb = new StringBuilder();
        // 遍历输入的每个字节
        for (byte b : bytes) {
            // 使用String.format将每个字节转换为两位十六进制字符，并追加到StringBuilder中
            // "%02x" 是一个格式化字符串，确保每个字节都被格式化为两位十六进制数，不足两位则前面补0
            sb.append(String.format("%02x", b));
        }
        // 返回构建好的十六进制字符串
        return sb.toString();
    }

}
